#include <petsc/private/dmpleximpl.h> /*I      "petscdmplex.h"   I*/
#include <petsc/private/hashmapi.h>

#ifdef PETSC_HAVE_EGADS
  #include <egads.h>
#endif

/* We need to understand how to natively parse STEP files. There seems to be only one open-source implementation of
   the STEP parser contained in the OpenCASCADE package. It is enough to make a strong man weep:

     https://github.com/tpaviot/oce/tree/master/src/STEPControl

   The STEP, and inner EXPRESS, formats are ISO standards, so they are documented

     https://stackoverflow.com/questions/26774037/documentation-or-specification-for-step-and-stp-files
     http://stepmod.sourceforge.net/express_model_spec/

   but again it seems that there has been a deliberate effort at obfuscation, probably to raise the bar for entrants.
*/

#ifdef PETSC_HAVE_EGADS
PETSC_INTERN PetscErrorCode DMSnapToGeomModel_EGADS_Internal(DM, PetscInt, ego, PetscInt, PetscInt, PetscInt, const PetscScalar[], PetscScalar[]);
PETSC_INTERN PetscErrorCode DMSnapToGeomModel_EGADSLite_Internal(DM, PetscInt, ego, PetscInt, PetscInt, PetscInt, const PetscScalar[], PetscScalar[]);

PetscErrorCode DMSnapToGeomModel_EGADS_Internal(DM dm, PetscInt p, ego model, PetscInt bodyID, PetscInt faceID, PetscInt edgeID, const PetscScalar mcoords[], PetscScalar gcoords[])
{
  DM   cdm;
  ego *bodies;
  ego  geom, body, obj;
  /* result has to hold derivatives, along with the value */
  double       params[3], result[18], paramsV[16 * 3], resultV[16 * 3], range[4];
  int          Nb, oclass, mtype, *senses, peri;
  Vec          coordinatesLocal;
  PetscScalar *coords = NULL;
  PetscInt     Nv, v, Np = 0, pm;
  PetscInt     dE, d;

  PetscFunctionBeginHot;
  PetscCall(DMGetCoordinateDM(dm, &cdm));
  PetscCall(DMGetCoordinateDim(dm, &dE));
  PetscCall(DMGetCoordinatesLocal(dm, &coordinatesLocal));
  PetscCall(EG_getTopology(model, &geom, &oclass, &mtype, NULL, &Nb, &bodies, &senses));
  PetscCheck(bodyID < Nb, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Body %" PetscInt_FMT " is not in [0, %d)", bodyID, Nb);
  body = bodies[bodyID];

  if (edgeID >= 0) {
    PetscCall(EG_objectBodyTopo(body, EDGE, edgeID, &obj));
    Np = 1;
  } else if (faceID >= 0) {
    PetscCall(EG_objectBodyTopo(body, FACE, faceID, &obj));
    Np = 2;
  } else {
    for (d = 0; d < dE; ++d) gcoords[d] = mcoords[d];
    PetscFunctionReturn(PETSC_SUCCESS);
  }

  /* Calculate parameters (t or u,v) for vertices */
  PetscCall(DMPlexVecGetClosure(cdm, NULL, coordinatesLocal, p, &Nv, &coords));
  Nv /= dE;
  if (Nv == 1) {
    PetscCall(DMPlexVecRestoreClosure(cdm, NULL, coordinatesLocal, p, &Nv, &coords));
    for (d = 0; d < dE; ++d) gcoords[d] = mcoords[d];
    PetscFunctionReturn(PETSC_SUCCESS);
  }
  PetscCheck(Nv <= 16, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Cannot handle %" PetscInt_FMT " coordinates associated to point %" PetscInt_FMT, Nv, p);

  /* Correct EGADSlite 2pi bug when calculating nearest point on Periodic Surfaces */
  PetscCall(EG_getRange(obj, range, &peri));
  for (v = 0; v < Nv; ++v) {
    PetscCall(EG_invEvaluate(obj, &coords[v * dE], &paramsV[v * 3], &resultV[v * 3]));
  #if 1
    if (peri > 0) {
      if (paramsV[v * 3 + 0] + 1.e-4 < range[0]) {
        paramsV[v * 3 + 0] += 2. * PETSC_PI;
      } else if (paramsV[v * 3 + 0] - 1.e-4 > range[1]) {
        paramsV[v * 3 + 0] -= 2. * PETSC_PI;
      }
    }
    if (peri > 1) {
      if (paramsV[v * 3 + 1] + 1.e-4 < range[2]) {
        paramsV[v * 3 + 1] += 2. * PETSC_PI;
      } else if (paramsV[v * 3 + 1] - 1.e-4 > range[3]) {
        paramsV[v * 3 + 1] -= 2. * PETSC_PI;
      }
    }
  #endif
  }
  PetscCall(DMPlexVecRestoreClosure(cdm, NULL, coordinatesLocal, p, &Nv, &coords));
  /* Calculate parameters (t or u,v) for new vertex at edge midpoint */
  for (pm = 0; pm < Np; ++pm) {
    params[pm] = 0.;
    for (v = 0; v < Nv; ++v) params[pm] += paramsV[v * 3 + pm];
    params[pm] /= Nv;
  }
  PetscCheck(!(params[0] < range[0]) && !(params[0] > range[1]), PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Point %" PetscInt_FMT " had bad interpolation", p);
  PetscCheck(Np <= 1 || (params[1] >= range[2] && params[1] <= range[3]), PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Point %" PetscInt_FMT " had bad interpolation", p);
  /* Put coordinates for new vertex in result[] */
  PetscCall(EG_evaluate(obj, params, result));
  for (d = 0; d < dE; ++d) gcoords[d] = result[d];
  PetscFunctionReturn(PETSC_SUCCESS);
}
#endif

PetscErrorCode DMSnapToGeomModel_EGADSLite(DM dm, PetscInt p, PetscInt dE, const PetscScalar mcoords[], PetscScalar gcoords[])
{
  PetscFunctionBeginHot;
#ifdef PETSC_HAVE_EGADS
  DMLabel        bodyLabel, faceLabel, edgeLabel;
  PetscInt       bodyID, faceID, edgeID;
  PetscContainer modelObj;
  ego            model;

  PetscCall(DMGetLabel(dm, "EGADS Body ID", &bodyLabel));
  PetscCall(DMGetLabel(dm, "EGADS Face ID", &faceLabel));
  PetscCall(DMGetLabel(dm, "EGADS Edge ID", &edgeLabel));
  PetscCheck(bodyLabel && faceLabel && edgeLabel, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "EGADSLite meshes must have body, face, and edge labels defined");
  PetscCall(PetscObjectQuery((PetscObject)dm, "EGADSLite Model", (PetscObject *)&modelObj));
  PetscCheck(modelObj, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "EGADSLite mesh missing model object");

  PetscCall(PetscContainerGetPointer(modelObj, (void **)&model));
  PetscCall(DMLabelGetValue(bodyLabel, p, &bodyID));
  PetscCall(DMLabelGetValue(faceLabel, p, &faceID));
  PetscCall(DMLabelGetValue(edgeLabel, p, &edgeID));
  /* Allows for "Connective" Plex Edges present in models with multiple non-touching Entities */
  if (bodyID < 0) {
    for (PetscInt d = 0; d < dE; ++d) gcoords[d] = mcoords[d];
    PetscFunctionReturn(PETSC_SUCCESS);
  }
  PetscCall(DMSnapToGeomModel_EGADSLite_Internal(dm, p, model, bodyID, faceID, edgeID, mcoords, gcoords));
#endif
  PetscFunctionReturn(PETSC_SUCCESS);
}

PetscErrorCode DMSnapToGeomModel_EGADS(DM dm, PetscInt p, PetscInt dE, const PetscScalar mcoords[], PetscScalar gcoords[])
{
  PetscFunctionBeginHot;
#ifdef PETSC_HAVE_EGADS
  DMLabel        bodyLabel, faceLabel, edgeLabel;
  PetscInt       bodyID, faceID, edgeID;
  PetscContainer modelObj;
  ego            model;

  PetscCall(DMGetLabel(dm, "EGADS Body ID", &bodyLabel));
  PetscCall(DMGetLabel(dm, "EGADS Face ID", &faceLabel));
  PetscCall(DMGetLabel(dm, "EGADS Edge ID", &edgeLabel));
  PetscCheck(bodyLabel && faceLabel && edgeLabel, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "EGADS meshes must have body, face, and edge labels defined");
  PetscCall(PetscObjectQuery((PetscObject)dm, "EGADS Model", (PetscObject *)&modelObj));
  PetscCheck(modelObj, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "EGADS mesh missing model object");

  PetscCall(PetscContainerGetPointer(modelObj, (void **)&model));
  PetscCall(DMLabelGetValue(bodyLabel, p, &bodyID));
  PetscCall(DMLabelGetValue(faceLabel, p, &faceID));
  PetscCall(DMLabelGetValue(edgeLabel, p, &edgeID));
  /* Allows for "Connective" Plex Edges present in models with multiple non-touching Entities */
  if (bodyID < 0) {
    for (PetscInt d = 0; d < dE; ++d) gcoords[d] = mcoords[d];
    PetscFunctionReturn(PETSC_SUCCESS);
  }
  PetscCall(DMSnapToGeomModel_EGADS_Internal(dm, p, model, bodyID, faceID, edgeID, mcoords, gcoords));
#endif
  PetscFunctionReturn(PETSC_SUCCESS);
}

#if defined(PETSC_HAVE_EGADS)
static PetscErrorCode DMPlexEGADSPrintModel_Internal(ego model)
{
  ego geom, *bodies, *objs, *nobjs, *mobjs, *lobjs;
  int oclass, mtype, *senses;
  int Nb, b;

  PetscFunctionBeginUser;
  /* test bodyTopo functions */
  PetscCall(EG_getTopology(model, &geom, &oclass, &mtype, NULL, &Nb, &bodies, &senses));
  PetscCall(PetscPrintf(PETSC_COMM_SELF, " Number of BODIES (nbodies): %d \n", Nb));

  for (b = 0; b < Nb; ++b) {
    ego body = bodies[b];
    int id, Nsh, Nf, Nl, l, Ne, e, Nv, v;

    /* Output Basic Model Topology */
    PetscCall(EG_getBodyTopos(body, NULL, SHELL, &Nsh, &objs));
    PetscCall(PetscPrintf(PETSC_COMM_SELF, "   Number of SHELLS: %d \n", Nsh));
    EG_free(objs);

    PetscCall(EG_getBodyTopos(body, NULL, FACE, &Nf, &objs));
    PetscCall(PetscPrintf(PETSC_COMM_SELF, "   Number of FACES: %d \n", Nf));
    EG_free(objs);

    PetscCall(EG_getBodyTopos(body, NULL, LOOP, &Nl, &lobjs));
    PetscCall(PetscPrintf(PETSC_COMM_SELF, "   Number of LOOPS: %d \n", Nl));

    PetscCall(EG_getBodyTopos(body, NULL, EDGE, &Ne, &objs));
    PetscCall(PetscPrintf(PETSC_COMM_SELF, "   Number of EDGES: %d \n", Ne));
    EG_free(objs);

    PetscCall(EG_getBodyTopos(body, NULL, NODE, &Nv, &objs));
    PetscCall(PetscPrintf(PETSC_COMM_SELF, "   Number of NODES: %d \n", Nv));
    EG_free(objs);

    for (l = 0; l < Nl; ++l) {
      ego loop = lobjs[l];

      id = EG_indexBodyTopo(body, loop);
      PetscCall(PetscPrintf(PETSC_COMM_SELF, "          LOOP ID: %d\n", id));

      /* Get EDGE info which associated with the current LOOP */
      PetscCall(EG_getTopology(loop, &geom, &oclass, &mtype, NULL, &Ne, &objs, &senses));

      for (e = 0; e < Ne; ++e) {
        ego    edge      = objs[e];
        double range[4]  = {0., 0., 0., 0.};
        double point[3]  = {0., 0., 0.};
        double params[3] = {0., 0., 0.};
        double result[18];
        int    peri;

        id = EG_indexBodyTopo(body, edge);
        PetscCall(PetscPrintf(PETSC_COMM_SELF, "            EDGE ID: %d (%d)\n", id, e));

        PetscCall(EG_getRange(edge, range, &peri));
        PetscCall(PetscPrintf(PETSC_COMM_SELF, "  Range = %lf, %lf, %lf, %lf \n", range[0], range[1], range[2], range[3]));

        /* Get NODE info which associated with the current EDGE */
        PetscCall(EG_getTopology(edge, &geom, &oclass, &mtype, NULL, &Nv, &nobjs, &senses));
        if (mtype == DEGENERATE) {
          PetscCall(PetscPrintf(PETSC_COMM_SELF, "  EDGE %d is DEGENERATE \n", id));
        } else {
          params[0] = range[0];
          PetscCall(EG_evaluate(edge, params, result));
          PetscCall(PetscPrintf(PETSC_COMM_SELF, "   between (%lf, %lf, %lf)", result[0], result[1], result[2]));
          params[0] = range[1];
          PetscCall(EG_evaluate(edge, params, result));
          PetscCall(PetscPrintf(PETSC_COMM_SELF, " and (%lf, %lf, %lf)\n", result[0], result[1], result[2]));
        }

        for (v = 0; v < Nv; ++v) {
          ego    vertex = nobjs[v];
          double limits[4];
          int    dummy;

          PetscCall(EG_getTopology(vertex, &geom, &oclass, &mtype, limits, &dummy, &mobjs, &senses));
          id = EG_indexBodyTopo(body, vertex);
          PetscCall(PetscPrintf(PETSC_COMM_SELF, "              NODE ID: %d \n", id));
          PetscCall(PetscPrintf(PETSC_COMM_SELF, "                 (x, y, z) = (%lf, %lf, %lf) \n", limits[0], limits[1], limits[2]));

          point[0] = point[0] + limits[0];
          point[1] = point[1] + limits[1];
          point[2] = point[2] + limits[2];
        }
      }
    }
    EG_free(lobjs);
  }
  PetscFunctionReturn(PETSC_SUCCESS);
}

static PetscErrorCode DMPlexEGADSDestroy_Private(void **context)
{
  if (context) EG_close((ego)*context);
  return 0;
}

static PetscErrorCode DMPlexCreateEGADS_Internal(MPI_Comm comm, ego context, ego model, DM *newdm)
{
  DMLabel  bodyLabel, faceLabel, edgeLabel, vertexLabel;
  PetscInt cStart, cEnd, c;
  /* EGADSLite variables */
  ego geom, *bodies, *objs, *nobjs, *mobjs, *lobjs;
  int oclass, mtype, nbodies, *senses;
  int b;
  /* PETSc variables */
  DM          dm;
  PetscHMapI  edgeMap = NULL;
  PetscInt    dim = -1, cdim = -1, numCorners = 0, maxCorners = 0, numVertices = 0, newVertices = 0, numEdges = 0, numCells = 0, newCells = 0, numQuads = 0, cOff = 0, fOff = 0;
  PetscInt   *cells = NULL, *cone = NULL;
  PetscReal  *coords = NULL;
  PetscMPIInt rank;

  PetscFunctionBegin;
  PetscCallMPI(MPI_Comm_rank(comm, &rank));
  if (rank == 0) {
    const PetscInt debug = 0;

    /* ---------------------------------------------------------------------------------------------------
    Generate Petsc Plex
      Get all Nodes in model, record coordinates in a correctly formatted array
      Cycle through bodies, cycle through loops, recorde NODE IDs in a correctly formatted array
      We need to uniformly refine the initial geometry to guarantee a valid mesh
    */

    /* Calculate cell and vertex sizes */
    PetscCall(EG_getTopology(model, &geom, &oclass, &mtype, NULL, &nbodies, &bodies, &senses));
    PetscCall(PetscHMapICreate(&edgeMap));
    numEdges = 0;
    for (b = 0; b < nbodies; ++b) {
      ego body = bodies[b];
      int id, Nl, l, Nv, v;

      PetscCall(EG_getBodyTopos(body, NULL, LOOP, &Nl, &lobjs));
      for (l = 0; l < Nl; ++l) {
        ego loop = lobjs[l];
        int Ner  = 0, Ne, e, Nc;

        PetscCall(EG_getTopology(loop, &geom, &oclass, &mtype, NULL, &Ne, &objs, &senses));
        for (e = 0; e < Ne; ++e) {
          ego           edge = objs[e];
          int           Nv, id;
          PetscHashIter iter;
          PetscBool     found;

          PetscCall(EG_getTopology(edge, &geom, &oclass, &mtype, NULL, &Nv, &nobjs, &senses));
          if (mtype == DEGENERATE) continue;
          id = EG_indexBodyTopo(body, edge);
          PetscCall(PetscHMapIFind(edgeMap, id - 1, &iter, &found));
          if (!found) PetscCall(PetscHMapISet(edgeMap, id - 1, numEdges++));
          ++Ner;
        }
        if (Ner == 2) {
          Nc = 2;
        } else if (Ner == 3) {
          Nc = 4;
        } else if (Ner == 4) {
          Nc = 8;
          ++numQuads;
        } else SETERRQ(PETSC_COMM_SELF, PETSC_ERR_SUP, "Cannot support loop with %d edges", Ner);
        numCells += Nc;
        newCells += Nc - 1;
        maxCorners = PetscMax(Ner * 2 + 1, maxCorners);
      }
      PetscCall(EG_getBodyTopos(body, NULL, NODE, &Nv, &nobjs));
      for (v = 0; v < Nv; ++v) {
        ego vertex = nobjs[v];

        id = EG_indexBodyTopo(body, vertex);
        /* TODO: Instead of assuming contiguous ids, we could use a hash table */
        numVertices = PetscMax(id, numVertices);
      }
      EG_free(lobjs);
      EG_free(nobjs);
    }
    PetscCall(PetscHMapIGetSize(edgeMap, &numEdges));
    newVertices = numEdges + numQuads;
    numVertices += newVertices;

    dim        = 2; /* Assume 3D Models :: Need to update to handle 2D Models in the future */
    cdim       = 3; /* Assume 3D Models :: Need to update to handle 2D Models in the future */
    numCorners = 3; /* Split cells into triangles */
    PetscCall(PetscMalloc3(numVertices * cdim, &coords, numCells * numCorners, &cells, maxCorners, &cone));

    /* Get vertex coordinates */
    for (b = 0; b < nbodies; ++b) {
      ego body = bodies[b];
      int id, Nv, v;

      PetscCall(EG_getBodyTopos(body, NULL, NODE, &Nv, &nobjs));
      for (v = 0; v < Nv; ++v) {
        ego    vertex = nobjs[v];
        double limits[4];
        int    dummy;

        PetscCall(EG_getTopology(vertex, &geom, &oclass, &mtype, limits, &dummy, &mobjs, &senses));
        id                          = EG_indexBodyTopo(body, vertex);
        coords[(id - 1) * cdim + 0] = limits[0];
        coords[(id - 1) * cdim + 1] = limits[1];
        coords[(id - 1) * cdim + 2] = limits[2];
      }
      EG_free(nobjs);
    }
    PetscCall(PetscHMapIClear(edgeMap));
    fOff     = numVertices - newVertices + numEdges;
    numEdges = 0;
    numQuads = 0;
    for (b = 0; b < nbodies; ++b) {
      ego body = bodies[b];
      int Nl, l;

      PetscCall(EG_getBodyTopos(body, NULL, LOOP, &Nl, &lobjs));
      for (l = 0; l < Nl; ++l) {
        ego loop = lobjs[l];
        int lid, Ner = 0, Ne, e;

        lid = EG_indexBodyTopo(body, loop);
        PetscCall(EG_getTopology(loop, &geom, &oclass, &mtype, NULL, &Ne, &objs, &senses));
        for (e = 0; e < Ne; ++e) {
          ego           edge = objs[e];
          int           eid, Nv;
          PetscHashIter iter;
          PetscBool     found;

          PetscCall(EG_getTopology(edge, &geom, &oclass, &mtype, NULL, &Nv, &nobjs, &senses));
          if (mtype == DEGENERATE) continue;
          ++Ner;
          eid = EG_indexBodyTopo(body, edge);
          PetscCall(PetscHMapIFind(edgeMap, eid - 1, &iter, &found));
          if (!found) {
            PetscInt v = numVertices - newVertices + numEdges;
            double   range[4], params[3] = {0., 0., 0.}, result[18];
            int      periodic[2];

            PetscCall(PetscHMapISet(edgeMap, eid - 1, numEdges++));
            PetscCall(EG_getRange(edge, range, periodic));
            params[0] = 0.5 * (range[0] + range[1]);
            PetscCall(EG_evaluate(edge, params, result));
            coords[v * cdim + 0] = result[0];
            coords[v * cdim + 1] = result[1];
            coords[v * cdim + 2] = result[2];
          }
        }
        if (Ner == 4) {
          PetscInt v = fOff + numQuads++;
          ego     *fobjs, face;
          double   range[4], params[3] = {0., 0., 0.}, result[18];
          int      Nf, fid, periodic[2];

          PetscCall(EG_getBodyTopos(body, loop, FACE, &Nf, &fobjs));
          face = fobjs[0];
          fid  = EG_indexBodyTopo(body, face);
          PetscCheck(Nf == 1, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Loop %d has %d faces, instead of 1 (%d)", lid - 1, Nf, fid);
          PetscCall(EG_getRange(face, range, periodic));
          params[0] = 0.5 * (range[0] + range[1]);
          params[1] = 0.5 * (range[2] + range[3]);
          PetscCall(EG_evaluate(face, params, result));
          coords[v * cdim + 0] = result[0];
          coords[v * cdim + 1] = result[1];
          coords[v * cdim + 2] = result[2];
        }
      }
    }
    PetscCheck(numEdges + numQuads == newVertices, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Number of new vertices %" PetscInt_FMT " != %" PetscInt_FMT " previous count", numEdges + numQuads, newVertices);

    /* Get cell vertices by traversing loops */
    numQuads = 0;
    cOff     = 0;
    for (b = 0; b < nbodies; ++b) {
      ego body = bodies[b];
      int id, Nl, l;

      PetscCall(EG_getBodyTopos(body, NULL, LOOP, &Nl, &lobjs));
      for (l = 0; l < Nl; ++l) {
        ego loop = lobjs[l];
        int lid, Ner = 0, Ne, e, nc = 0, c, Nt, t;

        lid = EG_indexBodyTopo(body, loop);
        PetscCall(EG_getTopology(loop, &geom, &oclass, &mtype, NULL, &Ne, &objs, &senses));

        for (e = 0; e < Ne; ++e) {
          ego edge = objs[e];
          int points[3];
          int eid, Nv, v, tmp;

          eid = EG_indexBodyTopo(body, edge);
          PetscCall(EG_getTopology(edge, &geom, &oclass, &mtype, NULL, &Nv, &nobjs, &senses));
          if (mtype == DEGENERATE) continue;
          else ++Ner;
          PetscCheck(Nv == 2, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Edge %d has %d vertices != 2", eid, Nv);

          for (v = 0; v < Nv; ++v) {
            ego vertex = nobjs[v];

            id            = EG_indexBodyTopo(body, vertex);
            points[v * 2] = id - 1;
          }
          {
            PetscInt edgeNum;

            PetscCall(PetscHMapIGet(edgeMap, eid - 1, &edgeNum));
            points[1] = numVertices - newVertices + edgeNum;
          }
          /* EGADS loops are not oriented, but seem to be in order, so we must piece them together */
          if (!nc) {
            for (v = 0; v < Nv + 1; ++v) cone[nc++] = points[v];
          } else {
            if (cone[nc - 1] == points[0]) {
              cone[nc++] = points[1];
              if (cone[0] != points[2]) cone[nc++] = points[2];
            } else if (cone[nc - 1] == points[2]) {
              cone[nc++] = points[1];
              if (cone[0] != points[0]) cone[nc++] = points[0];
            } else if (cone[nc - 3] == points[0]) {
              tmp          = cone[nc - 3];
              cone[nc - 3] = cone[nc - 1];
              cone[nc - 1] = tmp;
              cone[nc++]   = points[1];
              if (cone[0] != points[2]) cone[nc++] = points[2];
            } else if (cone[nc - 3] == points[2]) {
              tmp          = cone[nc - 3];
              cone[nc - 3] = cone[nc - 1];
              cone[nc - 1] = tmp;
              cone[nc++]   = points[1];
              if (cone[0] != points[0]) cone[nc++] = points[0];
            } else SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Edge %d does not match its predecessor", eid);
          }
        }
        PetscCheck(nc == 2 * Ner, PETSC_COMM_SELF, PETSC_ERR_ARG_SIZ, "Number of corners %" PetscInt_FMT " != %" PetscInt_FMT, nc, 2 * Ner);
        if (Ner == 4) cone[nc++] = numVertices - newVertices + numEdges + numQuads++;
        PetscCheck(nc <= maxCorners, PETSC_COMM_SELF, PETSC_ERR_ARG_SIZ, "Number of corners %" PetscInt_FMT " > %" PetscInt_FMT " max", nc, maxCorners);
        /* Triangulate the loop */
        switch (Ner) {
        case 2: /* Bi-Segment -> 2 triangles */
          Nt                           = 2;
          cells[cOff * numCorners + 0] = cone[0];
          cells[cOff * numCorners + 1] = cone[1];
          cells[cOff * numCorners + 2] = cone[2];
          ++cOff;
          cells[cOff * numCorners + 0] = cone[0];
          cells[cOff * numCorners + 1] = cone[2];
          cells[cOff * numCorners + 2] = cone[3];
          ++cOff;
          break;
        case 3: /* Triangle   -> 4 triangles */
          Nt                           = 4;
          cells[cOff * numCorners + 0] = cone[0];
          cells[cOff * numCorners + 1] = cone[1];
          cells[cOff * numCorners + 2] = cone[5];
          ++cOff;
          cells[cOff * numCorners + 0] = cone[1];
          cells[cOff * numCorners + 1] = cone[2];
          cells[cOff * numCorners + 2] = cone[3];
          ++cOff;
          cells[cOff * numCorners + 0] = cone[5];
          cells[cOff * numCorners + 1] = cone[3];
          cells[cOff * numCorners + 2] = cone[4];
          ++cOff;
          cells[cOff * numCorners + 0] = cone[1];
          cells[cOff * numCorners + 1] = cone[3];
          cells[cOff * numCorners + 2] = cone[5];
          ++cOff;
          break;
        case 4: /* Quad       -> 8 triangles */
          Nt                           = 8;
          cells[cOff * numCorners + 0] = cone[0];
          cells[cOff * numCorners + 1] = cone[1];
          cells[cOff * numCorners + 2] = cone[7];
          ++cOff;
          cells[cOff * numCorners + 0] = cone[1];
          cells[cOff * numCorners + 1] = cone[2];
          cells[cOff * numCorners + 2] = cone[3];
          ++cOff;
          cells[cOff * numCorners + 0] = cone[3];
          cells[cOff * numCorners + 1] = cone[4];
          cells[cOff * numCorners + 2] = cone[5];
          ++cOff;
          cells[cOff * numCorners + 0] = cone[5];
          cells[cOff * numCorners + 1] = cone[6];
          cells[cOff * numCorners + 2] = cone[7];
          ++cOff;
          cells[cOff * numCorners + 0] = cone[8];
          cells[cOff * numCorners + 1] = cone[1];
          cells[cOff * numCorners + 2] = cone[3];
          ++cOff;
          cells[cOff * numCorners + 0] = cone[8];
          cells[cOff * numCorners + 1] = cone[3];
          cells[cOff * numCorners + 2] = cone[5];
          ++cOff;
          cells[cOff * numCorners + 0] = cone[8];
          cells[cOff * numCorners + 1] = cone[5];
          cells[cOff * numCorners + 2] = cone[7];
          ++cOff;
          cells[cOff * numCorners + 0] = cone[8];
          cells[cOff * numCorners + 1] = cone[7];
          cells[cOff * numCorners + 2] = cone[1];
          ++cOff;
          break;
        default:
          SETERRQ(PETSC_COMM_SELF, PETSC_ERR_SUP, "Loop %d has %d edges, which we do not support", lid, Ner);
        }
        if (debug) {
          for (t = 0; t < Nt; ++t) {
            PetscCall(PetscPrintf(PETSC_COMM_SELF, "  LOOP Corner NODEs Triangle %" PetscInt_FMT " (", t));
            for (c = 0; c < numCorners; ++c) {
              if (c > 0) PetscCall(PetscPrintf(PETSC_COMM_SELF, ", "));
              PetscCall(PetscPrintf(PETSC_COMM_SELF, "%" PetscInt_FMT, cells[(cOff - Nt + t) * numCorners + c]));
            }
            PetscCall(PetscPrintf(PETSC_COMM_SELF, ")\n"));
          }
        }
      }
      EG_free(lobjs);
    }
  }
  PetscCheck(cOff == numCells, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Count of total cells %" PetscInt_FMT " != %" PetscInt_FMT " previous count", cOff, numCells);
  PetscCall(DMPlexCreateFromCellListPetsc(PETSC_COMM_WORLD, dim, numCells, numVertices, numCorners, PETSC_TRUE, cells, cdim, coords, &dm));
  PetscCall(PetscFree3(coords, cells, cone));
  PetscCall(PetscInfo(dm, " Total Number of Unique Cells    = %" PetscInt_FMT " (%" PetscInt_FMT ")\n", numCells, newCells));
  PetscCall(PetscInfo(dm, " Total Number of Unique Vertices = %" PetscInt_FMT " (%" PetscInt_FMT ")\n", numVertices, newVertices));
  /* Embed EGADS model in DM */
  {
    PetscContainer modelObj, contextObj;

    PetscCall(PetscContainerCreate(PETSC_COMM_SELF, &modelObj));
    PetscCall(PetscContainerSetPointer(modelObj, model));
    PetscCall(PetscObjectCompose((PetscObject)dm, "EGADS Model", (PetscObject)modelObj));
    PetscCall(PetscContainerDestroy(&modelObj));

    PetscCall(PetscContainerCreate(PETSC_COMM_SELF, &contextObj));
    PetscCall(PetscContainerSetPointer(contextObj, context));
    PetscCall(PetscContainerSetCtxDestroy(contextObj, DMPlexEGADSDestroy_Private));
    PetscCall(PetscObjectCompose((PetscObject)dm, "EGADS Context", (PetscObject)contextObj));
    PetscCall(PetscContainerDestroy(&contextObj));
  }
  /* Label points */
  PetscCall(DMCreateLabel(dm, "EGADS Body ID"));
  PetscCall(DMGetLabel(dm, "EGADS Body ID", &bodyLabel));
  PetscCall(DMCreateLabel(dm, "EGADS Face ID"));
  PetscCall(DMGetLabel(dm, "EGADS Face ID", &faceLabel));
  PetscCall(DMCreateLabel(dm, "EGADS Edge ID"));
  PetscCall(DMGetLabel(dm, "EGADS Edge ID", &edgeLabel));
  PetscCall(DMCreateLabel(dm, "EGADS Vertex ID"));
  PetscCall(DMGetLabel(dm, "EGADS Vertex ID", &vertexLabel));
  cOff = 0;
  for (b = 0; b < nbodies; ++b) {
    ego body = bodies[b];
    int id, Nl, l;

    PetscCall(EG_getBodyTopos(body, NULL, LOOP, &Nl, &lobjs));
    for (l = 0; l < Nl; ++l) {
      ego  loop = lobjs[l];
      ego *fobjs;
      int  lid, Nf, fid, Ner = 0, Ne, e, Nt = 0, t;

      lid = EG_indexBodyTopo(body, loop);
      PetscCall(EG_getBodyTopos(body, loop, FACE, &Nf, &fobjs));
      PetscCheck(Nf <= 1, PETSC_COMM_SELF, PETSC_ERR_SUP, "Loop %d has %d > 1 faces, which is not supported", lid, Nf);
      fid = EG_indexBodyTopo(body, fobjs[0]);
      EG_free(fobjs);
      PetscCall(EG_getTopology(loop, &geom, &oclass, &mtype, NULL, &Ne, &objs, &senses));
      for (e = 0; e < Ne; ++e) {
        ego             edge = objs[e];
        int             eid, Nv, v;
        PetscInt        points[3], support[2], numEdges, edgeNum;
        const PetscInt *edges;

        eid = EG_indexBodyTopo(body, edge);
        PetscCall(EG_getTopology(edge, &geom, &oclass, &mtype, NULL, &Nv, &nobjs, &senses));
        if (mtype == DEGENERATE) continue;
        else ++Ner;
        for (v = 0; v < Nv; ++v) {
          ego vertex = nobjs[v];

          id = EG_indexBodyTopo(body, vertex);
          PetscCall(DMLabelSetValue(edgeLabel, numCells + id - 1, eid));
          points[v * 2] = numCells + id - 1;
        }
        PetscCall(PetscHMapIGet(edgeMap, eid - 1, &edgeNum));
        points[1] = numCells + numVertices - newVertices + edgeNum;

        PetscCall(DMLabelSetValue(edgeLabel, points[1], eid));
        support[0] = points[0];
        support[1] = points[1];
        PetscCall(DMPlexGetJoin(dm, 2, support, &numEdges, &edges));
        PetscCheck(numEdges == 1, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Vertices (%" PetscInt_FMT ", %" PetscInt_FMT ") should only bound 1 edge, not %" PetscInt_FMT, support[0], support[1], numEdges);
        PetscCall(DMLabelSetValue(edgeLabel, edges[0], eid));
        PetscCall(DMPlexRestoreJoin(dm, 2, support, &numEdges, &edges));
        support[0] = points[1];
        support[1] = points[2];
        PetscCall(DMPlexGetJoin(dm, 2, support, &numEdges, &edges));
        PetscCheck(numEdges == 1, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Vertices (%" PetscInt_FMT ", %" PetscInt_FMT ") should only bound 1 edge, not %" PetscInt_FMT, support[0], support[1], numEdges);
        PetscCall(DMLabelSetValue(edgeLabel, edges[0], eid));
        PetscCall(DMPlexRestoreJoin(dm, 2, support, &numEdges, &edges));
      }
      switch (Ner) {
      case 2:
        Nt = 2;
        break;
      case 3:
        Nt = 4;
        break;
      case 4:
        Nt = 8;
        break;
      default:
        SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Loop with %d edges is unsupported", Ner);
      }
      for (t = 0; t < Nt; ++t) {
        PetscCall(DMLabelSetValue(bodyLabel, cOff + t, b));
        PetscCall(DMLabelSetValue(faceLabel, cOff + t, fid));
      }
      cOff += Nt;
    }
    EG_free(lobjs);
  }
  PetscCall(PetscHMapIDestroy(&edgeMap));
  PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
  for (c = cStart; c < cEnd; ++c) {
    PetscInt *closure = NULL;
    PetscInt  clSize, cl, bval, fval;

    PetscCall(DMPlexGetTransitiveClosure(dm, c, PETSC_TRUE, &clSize, &closure));
    PetscCall(DMLabelGetValue(bodyLabel, c, &bval));
    PetscCall(DMLabelGetValue(faceLabel, c, &fval));
    for (cl = 0; cl < clSize * 2; cl += 2) {
      PetscCall(DMLabelSetValue(bodyLabel, closure[cl], bval));
      PetscCall(DMLabelSetValue(faceLabel, closure[cl], fval));
    }
    PetscCall(DMPlexRestoreTransitiveClosure(dm, c, PETSC_TRUE, &clSize, &closure));
  }
  *newdm = dm;
  PetscFunctionReturn(PETSC_SUCCESS);
}

static PetscErrorCode DMPlexCreateEGADS(MPI_Comm comm, ego context, ego model, DM *newdm)
{
  DMLabel bodyLabel, faceLabel, edgeLabel, vertexLabel;
  // EGADS/EGADSLite variables
  ego geom, *bodies, *mobjs, *fobjs, *lobjs, *eobjs, *nobjs;
  ego topRef, prev, next;
  int oclass, mtype, nbodies, *senses, *lSenses, *eSenses;
  int b;
  // PETSc variables
  DM              dm;
  PetscHMapI      edgeMap = NULL, bodyIndexMap = NULL, bodyVertexMap = NULL, bodyEdgeMap = NULL, bodyFaceMap = NULL, bodyEdgeGlobalMap = NULL;
  PetscInt        dim = -1, cdim = -1, numCorners = 0, numVertices = 0, numEdges = 0, numFaces = 0, numCells = 0, edgeCntr = 0;
  PetscInt        cellCntr = 0, numPoints = 0;
  PetscInt       *cells  = NULL;
  const PetscInt *cone   = NULL;
  PetscReal      *coords = NULL;
  PetscMPIInt     rank;

  PetscFunctionBeginUser;
  PetscCallMPI(MPI_Comm_rank(comm, &rank));
  if (rank == 0) {
    // ---------------------------------------------------------------------------------------------------
    // Generate Petsc Plex
    //  Get all Nodes in model, record coordinates in a correctly formatted array
    //  Cycle through bodies, cycle through loops, recorde NODE IDs in a correctly formatted array
    //  We need to uniformly refine the initial geometry to guarantee a valid mesh

    // Calculate cell and vertex sizes
    PetscCall(EG_getTopology(model, &geom, &oclass, &mtype, NULL, &nbodies, &bodies, &senses));

    PetscCall(PetscHMapICreate(&edgeMap));
    PetscCall(PetscHMapICreate(&bodyIndexMap));
    PetscCall(PetscHMapICreate(&bodyVertexMap));
    PetscCall(PetscHMapICreate(&bodyEdgeMap));
    PetscCall(PetscHMapICreate(&bodyEdgeGlobalMap));
    PetscCall(PetscHMapICreate(&bodyFaceMap));

    for (b = 0; b < nbodies; ++b) {
      ego           body = bodies[b];
      int           Nf, Ne, Nv;
      PetscHashIter BIiter, BViter, BEiter, BEGiter, BFiter, EMiter;
      PetscBool     BIfound, BVfound, BEfound, BEGfound, BFfound, EMfound;

      PetscCall(PetscHMapIFind(bodyIndexMap, b, &BIiter, &BIfound));
      PetscCall(PetscHMapIFind(bodyVertexMap, b, &BViter, &BVfound));
      PetscCall(PetscHMapIFind(bodyEdgeMap, b, &BEiter, &BEfound));
      PetscCall(PetscHMapIFind(bodyEdgeGlobalMap, b, &BEGiter, &BEGfound));
      PetscCall(PetscHMapIFind(bodyFaceMap, b, &BFiter, &BFfound));

      if (!BIfound) PetscCall(PetscHMapISet(bodyIndexMap, b, numFaces + numEdges + numVertices));
      if (!BVfound) PetscCall(PetscHMapISet(bodyVertexMap, b, numVertices));
      if (!BEfound) PetscCall(PetscHMapISet(bodyEdgeMap, b, numEdges));
      if (!BEGfound) PetscCall(PetscHMapISet(bodyEdgeGlobalMap, b, edgeCntr));
      if (!BFfound) PetscCall(PetscHMapISet(bodyFaceMap, b, numFaces));

      PetscCall(EG_getBodyTopos(body, NULL, FACE, &Nf, &fobjs));
      PetscCall(EG_getBodyTopos(body, NULL, EDGE, &Ne, &eobjs));
      PetscCall(EG_getBodyTopos(body, NULL, NODE, &Nv, &nobjs));
      EG_free(fobjs);
      EG_free(eobjs);
      EG_free(nobjs);

      // Remove DEGENERATE EDGES from Edge count
      PetscCall(EG_getBodyTopos(body, NULL, EDGE, &Ne, &eobjs));
      int Netemp = 0;
      for (int e = 0; e < Ne; ++e) {
        ego edge = eobjs[e];
        int eid;

        PetscCall(EG_getInfo(edge, &oclass, &mtype, &topRef, &prev, &next));
        eid = EG_indexBodyTopo(body, edge);

        PetscCall(PetscHMapIFind(edgeMap, edgeCntr + eid - 1, &EMiter, &EMfound));
        if (mtype == DEGENERATE) {
          if (!EMfound) PetscCall(PetscHMapISet(edgeMap, edgeCntr + eid - 1, -1));
        } else {
          ++Netemp;
          if (!EMfound) PetscCall(PetscHMapISet(edgeMap, edgeCntr + eid - 1, Netemp));
        }
      }
      EG_free(eobjs);

      // Determine Number of Cells
      PetscCall(EG_getBodyTopos(body, NULL, FACE, &Nf, &fobjs));
      for (int f = 0; f < Nf; ++f) {
        ego face     = fobjs[f];
        int edgeTemp = 0;

        PetscCall(EG_getBodyTopos(body, face, EDGE, &Ne, &eobjs));
        for (int e = 0; e < Ne; ++e) {
          ego edge = eobjs[e];

          PetscCall(EG_getInfo(edge, &oclass, &mtype, &topRef, &prev, &next));
          if (mtype != DEGENERATE) ++edgeTemp;
        }
        numCells += (2 * edgeTemp);
        EG_free(eobjs);
      }
      EG_free(fobjs);

      numFaces += Nf;
      numEdges += Netemp;
      numVertices += Nv;
      edgeCntr += Ne;
    }

    // Set up basic DMPlex parameters
    dim        = 2;                                 // Assumes 3D Models :: Need to handle 2D models in the future
    cdim       = 3;                                 // Assumes 3D Models :: Need to update to handle 2D models in future
    numCorners = 3;                                 // Split Faces into triangles
    numPoints  = numVertices + numEdges + numFaces; // total number of coordinate points

    PetscCall(PetscMalloc2(numPoints * cdim, &coords, numCells * numCorners, &cells));

    // Get Vertex Coordinates and Set up Cells
    for (b = 0; b < nbodies; ++b) {
      ego           body = bodies[b];
      int           Nf, Ne, Nv;
      PetscInt      bodyVertexIndexStart, bodyEdgeIndexStart, bodyEdgeGlobalIndexStart, bodyFaceIndexStart;
      PetscHashIter BViter, BEiter, BEGiter, BFiter, EMiter;
      PetscBool     BVfound, BEfound, BEGfound, BFfound, EMfound;

      // Vertices on Current Body
      PetscCall(EG_getBodyTopos(body, NULL, NODE, &Nv, &nobjs));

      PetscCall(PetscHMapIFind(bodyVertexMap, b, &BViter, &BVfound));
      PetscCheck(BVfound, PETSC_COMM_SELF, PETSC_ERR_SUP, "Body %d not found in bodyVertexMap", b);
      PetscCall(PetscHMapIGet(bodyVertexMap, b, &bodyVertexIndexStart));

      for (int v = 0; v < Nv; ++v) {
        ego    vertex = nobjs[v];
        double limits[4];
        int    id, dummy;

        PetscCall(EG_getTopology(vertex, &geom, &oclass, &mtype, limits, &dummy, &mobjs, &senses));
        id = EG_indexBodyTopo(body, vertex);

        coords[(bodyVertexIndexStart + id - 1) * cdim + 0] = limits[0];
        coords[(bodyVertexIndexStart + id - 1) * cdim + 1] = limits[1];
        coords[(bodyVertexIndexStart + id - 1) * cdim + 2] = limits[2];
      }
      EG_free(nobjs);

      // Edge Midpoint Vertices on Current Body
      PetscCall(EG_getBodyTopos(body, NULL, EDGE, &Ne, &eobjs));

      PetscCall(PetscHMapIFind(bodyEdgeMap, b, &BEiter, &BEfound));
      PetscCheck(BEfound, PETSC_COMM_SELF, PETSC_ERR_SUP, "Body %d not found in bodyEdgeMap", b);
      PetscCall(PetscHMapIGet(bodyEdgeMap, b, &bodyEdgeIndexStart));

      PetscCall(PetscHMapIFind(bodyEdgeGlobalMap, b, &BEGiter, &BEGfound));
      PetscCheck(BEGfound, PETSC_COMM_SELF, PETSC_ERR_SUP, "Body %d not found in bodyEdgeGlobalMap", b);
      PetscCall(PetscHMapIGet(bodyEdgeGlobalMap, b, &bodyEdgeGlobalIndexStart));

      for (int e = 0; e < Ne; ++e) {
        ego    edge = eobjs[e];
        double range[2], avgt[1], cntrPnt[9];
        int    eid, eOffset;
        int    periodic;

        PetscCall(EG_getInfo(edge, &oclass, &mtype, &topRef, &prev, &next));
        if (mtype == DEGENERATE) continue;

        eid = EG_indexBodyTopo(body, edge);

        // get relative offset from globalEdgeID Vector
        PetscCall(PetscHMapIFind(edgeMap, bodyEdgeGlobalIndexStart + eid - 1, &EMiter, &EMfound));
        PetscCheck(EMfound, PETSC_COMM_SELF, PETSC_ERR_SUP, "Edge %d not found in edgeMap", bodyEdgeGlobalIndexStart + eid - 1);
        PetscCall(PetscHMapIGet(edgeMap, bodyEdgeGlobalIndexStart + eid - 1, &eOffset));

        PetscCall(EG_getRange(edge, range, &periodic));
        avgt[0] = (range[0] + range[1]) / 2.;

        PetscCall(EG_evaluate(edge, avgt, cntrPnt));
        coords[(numVertices + bodyEdgeIndexStart + eOffset - 1) * cdim + 0] = cntrPnt[0];
        coords[(numVertices + bodyEdgeIndexStart + eOffset - 1) * cdim + 1] = cntrPnt[1];
        coords[(numVertices + bodyEdgeIndexStart + eOffset - 1) * cdim + 2] = cntrPnt[2];
      }
      EG_free(eobjs);

      // Face Midpoint Vertices on Current Body
      PetscCall(EG_getBodyTopos(body, NULL, FACE, &Nf, &fobjs));

      PetscCall(PetscHMapIFind(bodyFaceMap, b, &BFiter, &BFfound));
      PetscCheck(BFfound, PETSC_COMM_SELF, PETSC_ERR_SUP, "Body %d not found in bodyFaceMap", b);
      PetscCall(PetscHMapIGet(bodyFaceMap, b, &bodyFaceIndexStart));

      for (int f = 0; f < Nf; ++f) {
        ego    face = fobjs[f];
        double range[4], avgUV[2], cntrPnt[18];
        int    peri, id;

        id = EG_indexBodyTopo(body, face);
        PetscCall(EG_getRange(face, range, &peri));

        avgUV[0] = (range[0] + range[1]) / 2.;
        avgUV[1] = (range[2] + range[3]) / 2.;
        PetscCall(EG_evaluate(face, avgUV, cntrPnt));

        coords[(numVertices + numEdges + bodyFaceIndexStart + id - 1) * cdim + 0] = cntrPnt[0];
        coords[(numVertices + numEdges + bodyFaceIndexStart + id - 1) * cdim + 1] = cntrPnt[1];
        coords[(numVertices + numEdges + bodyFaceIndexStart + id - 1) * cdim + 2] = cntrPnt[2];
      }
      EG_free(fobjs);

      // Define Cells :: Note - This could be incorporated in the Face Midpoint Vertices Loop but was kept separate for clarity
      PetscCall(EG_getBodyTopos(body, NULL, FACE, &Nf, &fobjs));
      for (int f = 0; f < Nf; ++f) {
        ego face = fobjs[f];
        int fID, midFaceID, midPntID, startID, endID, Nl;

        fID       = EG_indexBodyTopo(body, face);
        midFaceID = numVertices + numEdges + bodyFaceIndexStart + fID - 1;
        // Must Traverse Loop to ensure we have all necessary information like the sense (+/- 1) of the edges.
        // TODO :: Only handles single loop faces (No holes). The choices for handling multiloop faces are:
        //            1) Use the DMPlexCreateEGADSFromFile() with the -dm_plex_egads_with_tess = 1 option.
        //               This will use a default EGADS tessellation as an initial surface mesh.
        //            2) Create the initial surface mesh via a 2D mesher :: Currently not available (?future?)
        //               May I suggest the XXXX as a starting point?

        PetscCall(EG_getTopology(face, &geom, &oclass, &mtype, NULL, &Nl, &lobjs, &lSenses));

        PetscCheck(Nl <= 1, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Face has %d Loops. Can only handle Faces with 1 Loop. Please use --dm_plex_egads_with_tess = 1 Option", Nl);
        for (int l = 0; l < Nl; ++l) {
          ego loop = lobjs[l];

          PetscCall(EG_getTopology(loop, &geom, &oclass, &mtype, NULL, &Ne, &eobjs, &eSenses));
          for (int e = 0; e < Ne; ++e) {
            ego edge = eobjs[e];
            int eid, eOffset;

            PetscCall(EG_getInfo(edge, &oclass, &mtype, &topRef, &prev, &next));
            eid = EG_indexBodyTopo(body, edge);
            if (mtype == DEGENERATE) continue;

            // get relative offset from globalEdgeID Vector
            PetscCall(PetscHMapIFind(edgeMap, bodyEdgeGlobalIndexStart + eid - 1, &EMiter, &EMfound));
            PetscCheck(EMfound, PETSC_COMM_SELF, PETSC_ERR_SUP, "Edge %d of Body %d not found in edgeMap. Global Edge ID :: %d", eid, b, bodyEdgeGlobalIndexStart + eid - 1);
            PetscCall(PetscHMapIGet(edgeMap, bodyEdgeGlobalIndexStart + eid - 1, &eOffset));

            midPntID = numVertices + bodyEdgeIndexStart + eOffset - 1;

            PetscCall(EG_getTopology(edge, &geom, &oclass, &mtype, NULL, &Nv, &nobjs, &senses));

            if (eSenses[e] > 0) {
              startID = EG_indexBodyTopo(body, nobjs[0]);
              endID   = EG_indexBodyTopo(body, nobjs[1]);
            } else {
              startID = EG_indexBodyTopo(body, nobjs[1]);
              endID   = EG_indexBodyTopo(body, nobjs[0]);
            }

            // Define 2 Cells per Edge with correct orientation
            cells[cellCntr * numCorners + 0] = midFaceID;
            cells[cellCntr * numCorners + 1] = bodyVertexIndexStart + startID - 1;
            cells[cellCntr * numCorners + 2] = midPntID;

            cells[cellCntr * numCorners + 3] = midFaceID;
            cells[cellCntr * numCorners + 4] = midPntID;
            cells[cellCntr * numCorners + 5] = bodyVertexIndexStart + endID - 1;

            cellCntr = cellCntr + 2;
          }
        }
      }
      EG_free(fobjs);
    }
  }

  // Generate DMPlex
  PetscCall(DMPlexCreateFromCellListPetsc(PETSC_COMM_WORLD, dim, numCells, numPoints, numCorners, PETSC_TRUE, cells, cdim, coords, &dm));
  PetscCall(PetscFree2(coords, cells));
  PetscCall(PetscInfo(dm, " Total Number of Unique Cells    = %" PetscInt_FMT " \n", numCells));
  PetscCall(PetscInfo(dm, " Total Number of Unique Vertices = %" PetscInt_FMT " \n", numVertices));

  // Embed EGADS model in DM
  {
    PetscContainer modelObj, contextObj;

    PetscCall(PetscContainerCreate(PETSC_COMM_SELF, &modelObj));
    PetscCall(PetscContainerSetPointer(modelObj, model));
    PetscCall(PetscObjectCompose((PetscObject)dm, "EGADS Model", (PetscObject)modelObj));
    PetscCall(PetscContainerDestroy(&modelObj));

    PetscCall(PetscContainerCreate(PETSC_COMM_SELF, &contextObj));
    PetscCall(PetscContainerSetPointer(contextObj, context));
    PetscCall(PetscContainerSetCtxDestroy(contextObj, DMPlexEGADSDestroy_Private));
    PetscCall(PetscObjectCompose((PetscObject)dm, "EGADS Context", (PetscObject)contextObj));
    PetscCall(PetscContainerDestroy(&contextObj));
  }
  // Label points
  PetscInt nStart, nEnd;

  PetscCall(DMCreateLabel(dm, "EGADS Body ID"));
  PetscCall(DMGetLabel(dm, "EGADS Body ID", &bodyLabel));
  PetscCall(DMCreateLabel(dm, "EGADS Face ID"));
  PetscCall(DMGetLabel(dm, "EGADS Face ID", &faceLabel));
  PetscCall(DMCreateLabel(dm, "EGADS Edge ID"));
  PetscCall(DMGetLabel(dm, "EGADS Edge ID", &edgeLabel));
  PetscCall(DMCreateLabel(dm, "EGADS Vertex ID"));
  PetscCall(DMGetLabel(dm, "EGADS Vertex ID", &vertexLabel));

  PetscCall(DMPlexGetHeightStratum(dm, 2, &nStart, &nEnd));

  cellCntr = 0;
  for (b = 0; b < nbodies; ++b) {
    ego           body = bodies[b];
    int           Nv, Ne, Nf;
    PetscInt      bodyVertexIndexStart, bodyEdgeIndexStart, bodyEdgeGlobalIndexStart, bodyFaceIndexStart;
    PetscHashIter BViter, BEiter, BEGiter, BFiter, EMiter;
    PetscBool     BVfound, BEfound, BEGfound, BFfound, EMfound;

    PetscCall(PetscHMapIFind(bodyVertexMap, b, &BViter, &BVfound));
    PetscCheck(BVfound, PETSC_COMM_SELF, PETSC_ERR_SUP, "Body %d not found in bodyVertexMap", b);
    PetscCall(PetscHMapIGet(bodyVertexMap, b, &bodyVertexIndexStart));

    PetscCall(PetscHMapIFind(bodyEdgeMap, b, &BEiter, &BEfound));
    PetscCheck(BEfound, PETSC_COMM_SELF, PETSC_ERR_SUP, "Body %d not found in bodyEdgeMap", b);
    PetscCall(PetscHMapIGet(bodyEdgeMap, b, &bodyEdgeIndexStart));

    PetscCall(PetscHMapIFind(bodyFaceMap, b, &BFiter, &BFfound));
    PetscCheck(BFfound, PETSC_COMM_SELF, PETSC_ERR_SUP, "Body %d not found in bodyFaceMap", b);
    PetscCall(PetscHMapIGet(bodyFaceMap, b, &bodyFaceIndexStart));

    PetscCall(PetscHMapIFind(bodyEdgeGlobalMap, b, &BEGiter, &BEGfound));
    PetscCheck(BEGfound, PETSC_COMM_SELF, PETSC_ERR_SUP, "Body %d not found in bodyEdgeGlobalMap", b);
    PetscCall(PetscHMapIGet(bodyEdgeGlobalMap, b, &bodyEdgeGlobalIndexStart));

    PetscCall(EG_getBodyTopos(body, NULL, FACE, &Nf, &fobjs));
    for (int f = 0; f < Nf; ++f) {
      ego face = fobjs[f];
      int fID, Nl;

      fID = EG_indexBodyTopo(body, face);

      PetscCall(EG_getBodyTopos(body, face, LOOP, &Nl, &lobjs));
      for (int l = 0; l < Nl; ++l) {
        ego loop = lobjs[l];
        int lid;

        lid = EG_indexBodyTopo(body, loop);
        PetscCheck(Nl <= 1, PETSC_COMM_SELF, PETSC_ERR_SUP, "Loop %d has %d > 1 faces, which is not supported", lid, Nf);

        PetscCall(EG_getTopology(loop, &geom, &oclass, &mtype, NULL, &Ne, &eobjs, &eSenses));
        for (int e = 0; e < Ne; ++e) {
          ego edge = eobjs[e];
          int eid, eOffset;

          // Skip DEGENERATE Edges
          PetscCall(EG_getInfo(edge, &oclass, &mtype, &topRef, &prev, &next));
          if (mtype == DEGENERATE) continue;
          eid = EG_indexBodyTopo(body, edge);

          // get relative offset from globalEdgeID Vector
          PetscCall(PetscHMapIFind(edgeMap, bodyEdgeGlobalIndexStart + eid - 1, &EMiter, &EMfound));
          PetscCheck(EMfound, PETSC_COMM_SELF, PETSC_ERR_SUP, "Edge %d of Body %d not found in edgeMap. Global Edge ID :: %d", eid, b, bodyEdgeGlobalIndexStart + eid - 1);
          PetscCall(PetscHMapIGet(edgeMap, bodyEdgeGlobalIndexStart + eid - 1, &eOffset));

          PetscCall(EG_getBodyTopos(body, edge, NODE, &Nv, &nobjs));
          for (int v = 0; v < Nv; ++v) {
            ego vertex = nobjs[v];
            int vID;

            vID = EG_indexBodyTopo(body, vertex);
            PetscCall(DMLabelSetValue(bodyLabel, nStart + bodyVertexIndexStart + vID - 1, b));
            PetscCall(DMLabelSetValue(vertexLabel, nStart + bodyVertexIndexStart + vID - 1, vID));
          }
          EG_free(nobjs);

          PetscCall(DMLabelSetValue(bodyLabel, nStart + numVertices + bodyEdgeIndexStart + eOffset - 1, b));
          PetscCall(DMLabelSetValue(edgeLabel, nStart + numVertices + bodyEdgeIndexStart + eOffset - 1, eid));

          // Define Cell faces
          for (int jj = 0; jj < 2; ++jj) {
            PetscCall(DMLabelSetValue(bodyLabel, cellCntr, b));
            PetscCall(DMLabelSetValue(faceLabel, cellCntr, fID));
            PetscCall(DMPlexGetCone(dm, cellCntr, &cone));

            PetscCall(DMLabelSetValue(bodyLabel, cone[0], b));
            PetscCall(DMLabelSetValue(faceLabel, cone[0], fID));

            PetscCall(DMLabelSetValue(bodyLabel, cone[1], b));
            PetscCall(DMLabelSetValue(edgeLabel, cone[1], eid));

            PetscCall(DMLabelSetValue(bodyLabel, cone[2], b));
            PetscCall(DMLabelSetValue(faceLabel, cone[2], fID));

            cellCntr = cellCntr + 1;
          }
        }
      }
      EG_free(lobjs);

      PetscCall(DMLabelSetValue(bodyLabel, nStart + numVertices + numEdges + bodyFaceIndexStart + fID - 1, b));
      PetscCall(DMLabelSetValue(faceLabel, nStart + numVertices + numEdges + bodyFaceIndexStart + fID - 1, fID));
    }
    EG_free(fobjs);
  }

  PetscCall(PetscHMapIDestroy(&edgeMap));
  PetscCall(PetscHMapIDestroy(&bodyIndexMap));
  PetscCall(PetscHMapIDestroy(&bodyVertexMap));
  PetscCall(PetscHMapIDestroy(&bodyEdgeMap));
  PetscCall(PetscHMapIDestroy(&bodyEdgeGlobalMap));
  PetscCall(PetscHMapIDestroy(&bodyFaceMap));

  *newdm = dm;
  PetscFunctionReturn(PETSC_SUCCESS);
}

static PetscErrorCode DMPlexCreateEGADS_Tess_Internal(MPI_Comm comm, ego context, ego model, DM *newdm)
{
  DMLabel bodyLabel, faceLabel, edgeLabel, vertexLabel;
  /* EGADSLite variables */
  ego    geom, *bodies, *fobjs;
  int    b, oclass, mtype, nbodies, *senses;
  int    totalNumTris = 0, totalNumPoints = 0;
  double boundBox[6] = {0., 0., 0., 0., 0., 0.}, tessSize;
  /* PETSc variables */
  DM              dm;
  PetscHMapI      pointIndexStartMap = NULL, triIndexStartMap = NULL, pTypeLabelMap = NULL, pIndexLabelMap = NULL;
  PetscHMapI      pBodyIndexLabelMap = NULL, triFaceIDLabelMap = NULL, triBodyIDLabelMap = NULL;
  PetscInt        dim = -1, cdim = -1, numCorners = 0, counter = 0;
  PetscInt       *cells  = NULL;
  const PetscInt *cone   = NULL;
  PetscReal      *coords = NULL;
  PetscMPIInt     rank;

  PetscFunctionBeginUser;
  PetscCallMPI(MPI_Comm_rank(comm, &rank));
  if (rank == 0) {
    // ---------------------------------------------------------------------------------------------------
    // Generate Petsc Plex from EGADSlite created Tessellation of geometry
    // ---------------------------------------------------------------------------------------------------

    // Calculate cell and vertex sizes
    PetscCall(EG_getTopology(model, &geom, &oclass, &mtype, NULL, &nbodies, &bodies, &senses));

    PetscCall(PetscHMapICreate(&pointIndexStartMap));
    PetscCall(PetscHMapICreate(&triIndexStartMap));
    PetscCall(PetscHMapICreate(&pTypeLabelMap));
    PetscCall(PetscHMapICreate(&pIndexLabelMap));
    PetscCall(PetscHMapICreate(&pBodyIndexLabelMap));
    PetscCall(PetscHMapICreate(&triFaceIDLabelMap));
    PetscCall(PetscHMapICreate(&triBodyIDLabelMap));

    /* Create Tessellation of Bodies */
    ego tessArray[nbodies];

    for (b = 0; b < nbodies; ++b) {
      ego           body      = bodies[b];
      double        params[3] = {0.0, 0.0, 0.0}; // Parameters for Tessellation
      int           Nf, bodyNumPoints = 0, bodyNumTris = 0;
      PetscHashIter PISiter, TISiter;
      PetscBool     PISfound, TISfound;

      /* Store Start Index for each Body's Point and Tris */
      PetscCall(PetscHMapIFind(pointIndexStartMap, b, &PISiter, &PISfound));
      PetscCall(PetscHMapIFind(triIndexStartMap, b, &TISiter, &TISfound));

      if (!PISfound) PetscCall(PetscHMapISet(pointIndexStartMap, b, totalNumPoints));
      if (!TISfound) PetscCall(PetscHMapISet(triIndexStartMap, b, totalNumTris));

      /* Calculate Tessellation parameters based on Bounding Box */
      /* Get Bounding Box Dimensions of the BODY */
      PetscCall(EG_getBoundingBox(body, boundBox));
      tessSize = boundBox[3] - boundBox[0];
      if (tessSize < boundBox[4] - boundBox[1]) tessSize = boundBox[4] - boundBox[1];
      if (tessSize < boundBox[5] - boundBox[2]) tessSize = boundBox[5] - boundBox[2];

      // TODO :: May want to give users tessellation parameter options //
      params[0] = 0.0250 * tessSize;
      params[1] = 0.0075 * tessSize;
      params[2] = 15.0;

      PetscCall(EG_makeTessBody(body, params, &tessArray[b]));

      PetscCall(EG_getBodyTopos(body, NULL, FACE, &Nf, &fobjs));

      for (int f = 0; f < Nf; ++f) {
        ego           face = fobjs[f];
        int           len, fID, ntris;
        const int    *ptype, *pindex, *ptris, *ptric;
        const double *pxyz, *puv;

        // Get Face ID //
        fID = EG_indexBodyTopo(body, face);

        // Checkout the Surface Tessellation //
        PetscCall(EG_getTessFace(tessArray[b], fID, &len, &pxyz, &puv, &ptype, &pindex, &ntris, &ptris, &ptric));

        // Determine total number of triangle cells in the tessellation //
        bodyNumTris += (int)ntris;

        // Check out the point index and coordinate //
        for (int p = 0; p < len; ++p) {
          int global;

          PetscCall(EG_localToGlobal(tessArray[b], fID, p + 1, &global));

          // Determine the total number of points in the tessellation //
          bodyNumPoints = PetscMax(bodyNumPoints, global);
        }
      }
      EG_free(fobjs);

      totalNumPoints += bodyNumPoints;
      totalNumTris += bodyNumTris;
    }
    //}  - Original End of (rank == 0)

    dim        = 2;
    cdim       = 3;
    numCorners = 3;
    //PetscInt counter = 0;

    /* NEED TO DEFINE MATRICES/VECTORS TO STORE GEOM REFERENCE DATA   */
    /* Fill in below and use to define DMLabels after DMPlex creation */
    PetscCall(PetscMalloc2(totalNumPoints * cdim, &coords, totalNumTris * numCorners, &cells));

    for (b = 0; b < nbodies; ++b) {
      ego           body = bodies[b];
      int           Nf;
      PetscInt      pointIndexStart;
      PetscHashIter PISiter;
      PetscBool     PISfound;

      PetscCall(PetscHMapIFind(pointIndexStartMap, b, &PISiter, &PISfound));
      PetscCheck(PISfound, PETSC_COMM_SELF, PETSC_ERR_SUP, "Body %d not found in pointIndexStartMap", b);
      PetscCall(PetscHMapIGet(pointIndexStartMap, b, &pointIndexStart));

      PetscCall(EG_getBodyTopos(body, NULL, FACE, &Nf, &fobjs));

      for (int f = 0; f < Nf; ++f) {
        /* Get Face Object */
        ego           face = fobjs[f];
        int           len, fID, ntris;
        const int    *ptype, *pindex, *ptris, *ptric;
        const double *pxyz, *puv;

        /* Get Face ID */
        fID = EG_indexBodyTopo(body, face);

        /* Checkout the Surface Tessellation */
        PetscCall(EG_getTessFace(tessArray[b], fID, &len, &pxyz, &puv, &ptype, &pindex, &ntris, &ptris, &ptric));

        /* Check out the point index and coordinate */
        for (int p = 0; p < len; ++p) {
          int           global;
          PetscHashIter PTLiter, PILiter, PBLiter;
          PetscBool     PTLfound, PILfound, PBLfound;

          PetscCall(EG_localToGlobal(tessArray[b], fID, p + 1, &global));

          /* Set the coordinates array for DAG */
          coords[((global - 1 + pointIndexStart) * 3) + 0] = pxyz[(p * 3) + 0];
          coords[((global - 1 + pointIndexStart) * 3) + 1] = pxyz[(p * 3) + 1];
          coords[((global - 1 + pointIndexStart) * 3) + 2] = pxyz[(p * 3) + 2];

          /* Store Geometry Label Information for DMLabel assignment later */
          PetscCall(PetscHMapIFind(pTypeLabelMap, global - 1 + pointIndexStart, &PTLiter, &PTLfound));
          PetscCall(PetscHMapIFind(pIndexLabelMap, global - 1 + pointIndexStart, &PILiter, &PILfound));
          PetscCall(PetscHMapIFind(pBodyIndexLabelMap, global - 1 + pointIndexStart, &PBLiter, &PBLfound));

          if (!PTLfound) PetscCall(PetscHMapISet(pTypeLabelMap, global - 1 + pointIndexStart, ptype[p]));
          if (!PILfound) PetscCall(PetscHMapISet(pIndexLabelMap, global - 1 + pointIndexStart, pindex[p]));
          if (!PBLfound) PetscCall(PetscHMapISet(pBodyIndexLabelMap, global - 1 + pointIndexStart, b));

          if (ptype[p] < 0) PetscCall(PetscHMapISet(pIndexLabelMap, global - 1 + pointIndexStart, fID));
        }

        for (int t = 0; t < (int)ntris; ++t) {
          int           global, globalA, globalB;
          PetscHashIter TFLiter, TBLiter;
          PetscBool     TFLfound, TBLfound;

          PetscCall(EG_localToGlobal(tessArray[b], fID, ptris[(t * 3) + 0], &global));
          cells[(counter * 3) + 0] = global - 1 + pointIndexStart;

          PetscCall(EG_localToGlobal(tessArray[b], fID, ptris[(t * 3) + 1], &globalA));
          cells[(counter * 3) + 1] = globalA - 1 + pointIndexStart;

          PetscCall(EG_localToGlobal(tessArray[b], fID, ptris[(t * 3) + 2], &globalB));
          cells[(counter * 3) + 2] = globalB - 1 + pointIndexStart;

          PetscCall(PetscHMapIFind(triFaceIDLabelMap, counter, &TFLiter, &TFLfound));
          PetscCall(PetscHMapIFind(triBodyIDLabelMap, counter, &TBLiter, &TBLfound));

          if (!TFLfound) PetscCall(PetscHMapISet(triFaceIDLabelMap, counter, fID));
          if (!TBLfound) PetscCall(PetscHMapISet(triBodyIDLabelMap, counter, b));

          counter += 1;
        }
      }
      EG_free(fobjs);
    }
  }

  //Build DMPlex
  PetscCall(DMPlexCreateFromCellListPetsc(PETSC_COMM_WORLD, dim, totalNumTris, totalNumPoints, numCorners, PETSC_TRUE, cells, cdim, coords, &dm));
  PetscCall(PetscFree2(coords, cells));

  // Embed EGADS model in DM
  {
    PetscContainer modelObj, contextObj;

    PetscCall(PetscContainerCreate(PETSC_COMM_SELF, &modelObj));
    PetscCall(PetscContainerSetPointer(modelObj, model));
    PetscCall(PetscObjectCompose((PetscObject)dm, "EGADS Model", (PetscObject)modelObj));
    PetscCall(PetscContainerDestroy(&modelObj));

    PetscCall(PetscContainerCreate(PETSC_COMM_SELF, &contextObj));
    PetscCall(PetscContainerSetPointer(contextObj, context));
    PetscCall(PetscContainerSetCtxDestroy(contextObj, DMPlexEGADSDestroy_Private));
    PetscCall(PetscObjectCompose((PetscObject)dm, "EGADS Context", (PetscObject)contextObj));
    PetscCall(PetscContainerDestroy(&contextObj));
  }

  // Label Points
  PetscCall(DMCreateLabel(dm, "EGADS Body ID"));
  PetscCall(DMGetLabel(dm, "EGADS Body ID", &bodyLabel));
  PetscCall(DMCreateLabel(dm, "EGADS Face ID"));
  PetscCall(DMGetLabel(dm, "EGADS Face ID", &faceLabel));
  PetscCall(DMCreateLabel(dm, "EGADS Edge ID"));
  PetscCall(DMGetLabel(dm, "EGADS Edge ID", &edgeLabel));
  PetscCall(DMCreateLabel(dm, "EGADS Vertex ID"));
  PetscCall(DMGetLabel(dm, "EGADS Vertex ID", &vertexLabel));

  /* Get Number of DAG Nodes at each level */
  int fStart, fEnd, eStart, eEnd, nStart, nEnd;

  PetscCall(DMPlexGetHeightStratum(dm, 0, &fStart, &fEnd));
  PetscCall(DMPlexGetHeightStratum(dm, 1, &eStart, &eEnd));
  PetscCall(DMPlexGetHeightStratum(dm, 2, &nStart, &nEnd));

  /* Set DMLabels for NODES */
  for (int n = nStart; n < nEnd; ++n) {
    int           pTypeVal, pIndexVal, pBodyVal;
    PetscHashIter PTLiter, PILiter, PBLiter;
    PetscBool     PTLfound, PILfound, PBLfound;

    //Converted to Hash Tables
    PetscCall(PetscHMapIFind(pTypeLabelMap, n - nStart, &PTLiter, &PTLfound));
    PetscCheck(PTLfound, PETSC_COMM_SELF, PETSC_ERR_SUP, "DAG Point %d not found in pTypeLabelMap", n);
    PetscCall(PetscHMapIGet(pTypeLabelMap, n - nStart, &pTypeVal));

    PetscCall(PetscHMapIFind(pIndexLabelMap, n - nStart, &PILiter, &PILfound));
    PetscCheck(PILfound, PETSC_COMM_SELF, PETSC_ERR_SUP, "DAG Point %d not found in pIndexLabelMap", n);
    PetscCall(PetscHMapIGet(pIndexLabelMap, n - nStart, &pIndexVal));

    PetscCall(PetscHMapIFind(pBodyIndexLabelMap, n - nStart, &PBLiter, &PBLfound));
    PetscCheck(PBLfound, PETSC_COMM_SELF, PETSC_ERR_SUP, "DAG Point %d not found in pBodyLabelMap", n);
    PetscCall(PetscHMapIGet(pBodyIndexLabelMap, n - nStart, &pBodyVal));

    PetscCall(DMLabelSetValue(bodyLabel, n, pBodyVal));
    if (pTypeVal == 0) PetscCall(DMLabelSetValue(vertexLabel, n, pIndexVal));
    if (pTypeVal > 0) PetscCall(DMLabelSetValue(edgeLabel, n, pIndexVal));
    if (pTypeVal < 0) PetscCall(DMLabelSetValue(faceLabel, n, pIndexVal));
  }

  /* Set DMLabels for Edges - Based on the DMLabels of the EDGE's NODES */
  for (int e = eStart; e < eEnd; ++e) {
    int bodyID_0, vertexID_0, vertexID_1, edgeID_0, edgeID_1, faceID_0, faceID_1;

    PetscCall(DMPlexGetCone(dm, e, &cone));
    PetscCall(DMLabelGetValue(bodyLabel, cone[0], &bodyID_0)); // Do I need to check the other end?
    PetscCall(DMLabelGetValue(vertexLabel, cone[0], &vertexID_0));
    PetscCall(DMLabelGetValue(vertexLabel, cone[1], &vertexID_1));
    PetscCall(DMLabelGetValue(edgeLabel, cone[0], &edgeID_0));
    PetscCall(DMLabelGetValue(edgeLabel, cone[1], &edgeID_1));
    PetscCall(DMLabelGetValue(faceLabel, cone[0], &faceID_0));
    PetscCall(DMLabelGetValue(faceLabel, cone[1], &faceID_1));

    PetscCall(DMLabelSetValue(bodyLabel, e, bodyID_0));

    if (edgeID_0 == edgeID_1) PetscCall(DMLabelSetValue(edgeLabel, e, edgeID_0));
    else if (vertexID_0 > 0 && edgeID_1 > 0) PetscCall(DMLabelSetValue(edgeLabel, e, edgeID_1));
    else if (vertexID_1 > 0 && edgeID_0 > 0) PetscCall(DMLabelSetValue(edgeLabel, e, edgeID_0));
    else { /* Do Nothing */ }
  }

  /* Set DMLabels for Cells */
  for (int f = fStart; f < fEnd; ++f) {
    int           edgeID_0;
    PetscInt      triBodyVal, triFaceVal;
    PetscHashIter TFLiter, TBLiter;
    PetscBool     TFLfound, TBLfound;

    // Convert to Hash Table
    PetscCall(PetscHMapIFind(triFaceIDLabelMap, f - fStart, &TFLiter, &TFLfound));
    PetscCheck(TFLfound, PETSC_COMM_SELF, PETSC_ERR_SUP, "DAG Point %d not found in triFaceIDLabelMap", f);
    PetscCall(PetscHMapIGet(triFaceIDLabelMap, f - fStart, &triFaceVal));

    PetscCall(PetscHMapIFind(triBodyIDLabelMap, f - fStart, &TBLiter, &TBLfound));
    PetscCheck(TBLfound, PETSC_COMM_SELF, PETSC_ERR_SUP, "DAG Point %d not found in triBodyIDLabelMap", f);
    PetscCall(PetscHMapIGet(triBodyIDLabelMap, f - fStart, &triBodyVal));

    PetscCall(DMLabelSetValue(bodyLabel, f, triBodyVal));
    PetscCall(DMLabelSetValue(faceLabel, f, triFaceVal));

    /* Finish Labeling previously unlabeled DMPlex Edges - Assumes Triangular Cell (3 Edges Max) */
    PetscCall(DMPlexGetCone(dm, f, &cone));

    for (int jj = 0; jj < 3; ++jj) {
      PetscCall(DMLabelGetValue(edgeLabel, cone[jj], &edgeID_0));

      if (edgeID_0 < 0) {
        PetscCall(DMLabelSetValue(bodyLabel, cone[jj], triBodyVal));
        PetscCall(DMLabelSetValue(faceLabel, cone[jj], triFaceVal));
      }
    }
  }

  *newdm = dm;
  PetscFunctionReturn(PETSC_SUCCESS);
}
#endif

/*@
  DMPlexInflateToGeomModel - Snaps the vertex coordinates of a `DMPLEX` object representing the mesh to its geometry if some vertices depart from the model.
  This usually happens with non-conforming refinement.

  Collective

  Input Parameter:
. dm - The uninflated `DM` object representing the mesh

  Level: intermediate

.seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMCreate()`, `DMPlexCreateEGADS()`
@*/
PetscErrorCode DMPlexInflateToGeomModel(DM dm)
{
#if defined(PETSC_HAVE_EGADS)
  /* EGADS Variables */
  ego    model, geom, body, face, edge;
  ego   *bodies;
  int    Nb, oclass, mtype, *senses;
  double result[3];
  /* PETSc Variables */
  DM             cdm;
  PetscContainer modelObj;
  DMLabel        bodyLabel, faceLabel, edgeLabel, vertexLabel;
  Vec            coordinates;
  PetscScalar   *coords;
  PetscInt       bodyID, faceID, edgeID, vertexID;
  PetscInt       cdim, d, vStart, vEnd, v;
#endif

  PetscFunctionBegin;
#if defined(PETSC_HAVE_EGADS)
  PetscCall(PetscObjectQuery((PetscObject)dm, "EGADS Model", (PetscObject *)&modelObj));
  if (!modelObj) PetscFunctionReturn(PETSC_SUCCESS);
  PetscCall(DMGetCoordinateDim(dm, &cdim));
  PetscCall(DMGetCoordinateDM(dm, &cdm));
  PetscCall(DMGetCoordinatesLocal(dm, &coordinates));
  PetscCall(DMGetLabel(dm, "EGADS Body ID", &bodyLabel));
  PetscCall(DMGetLabel(dm, "EGADS Face ID", &faceLabel));
  PetscCall(DMGetLabel(dm, "EGADS Edge ID", &edgeLabel));
  PetscCall(DMGetLabel(dm, "EGADS Vertex ID", &vertexLabel));

  PetscCall(PetscContainerGetPointer(modelObj, (void **)&model));
  PetscCall(EG_getTopology(model, &geom, &oclass, &mtype, NULL, &Nb, &bodies, &senses));

  PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
  PetscCall(VecGetArrayWrite(coordinates, &coords));
  for (v = vStart; v < vEnd; ++v) {
    PetscScalar *vcoords;

    PetscCall(DMLabelGetValue(bodyLabel, v, &bodyID));
    PetscCall(DMLabelGetValue(faceLabel, v, &faceID));
    PetscCall(DMLabelGetValue(edgeLabel, v, &edgeID));
    PetscCall(DMLabelGetValue(vertexLabel, v, &vertexID));

    PetscCheck(bodyID < Nb, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Body %" PetscInt_FMT " is not in [0, %d)", bodyID, Nb);
    body = bodies[bodyID];

    PetscCall(DMPlexPointLocalRef(cdm, v, coords, (void *)&vcoords));
    if (edgeID > 0) {
      /* Snap to EDGE at nearest location */
      double params[1];
      PetscCall(EG_objectBodyTopo(body, EDGE, edgeID, &edge));
      PetscCall(EG_invEvaluate(edge, vcoords, params, result)); // Get (x,y,z) of nearest point on EDGE
      for (d = 0; d < cdim; ++d) vcoords[d] = result[d];
    } else if (faceID > 0) {
      /* Snap to FACE at nearest location */
      double params[2];
      PetscCall(EG_objectBodyTopo(body, FACE, faceID, &face));
      PetscCall(EG_invEvaluate(face, vcoords, params, result)); // Get (x,y,z) of nearest point on FACE
      for (d = 0; d < cdim; ++d) vcoords[d] = result[d];
    }
  }
  PetscCall(VecRestoreArrayWrite(coordinates, &coords));
  /* Clear out global coordinates */
  PetscCall(VecDestroy(&dm->coordinates[0].x));
#endif
  PetscFunctionReturn(PETSC_SUCCESS);
}

/*@
  DMPlexCreateEGADSFromFile - Create a `DMPLEX` mesh from an EGADS, IGES, or STEP file.

  Collective

  Input Parameters:
+ comm     - The MPI communicator
- filename - The name of the EGADS, IGES, or STEP file

  Output Parameter:
. dm - The `DM` object representing the mesh

  Level: beginner

.seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMCreate()`, `DMPlexCreateEGADS()`, `DMPlexCreateEGADSLiteFromFile()`
@*/
PetscErrorCode DMPlexCreateEGADSFromFile(MPI_Comm comm, const char filename[], DM *dm)
{
  PetscMPIInt rank;
#if defined(PETSC_HAVE_EGADS)
  ego context = NULL, model = NULL;
#endif
  PetscBool printModel = PETSC_FALSE, tessModel = PETSC_FALSE, newModel = PETSC_FALSE;

  PetscFunctionBegin;
  PetscAssertPointer(filename, 2);
  PetscCall(PetscOptionsGetBool(NULL, NULL, "-dm_plex_egads_print_model", &printModel, NULL));
  PetscCall(PetscOptionsGetBool(NULL, NULL, "-dm_plex_egads_tess_model", &tessModel, NULL));
  PetscCall(PetscOptionsGetBool(NULL, NULL, "-dm_plex_egads_new_model", &newModel, NULL));
  PetscCallMPI(MPI_Comm_rank(comm, &rank));
#if defined(PETSC_HAVE_EGADS)
  if (rank == 0) {
    PetscCall(EG_open(&context));
    PetscCall(EG_loadModel(context, 0, filename, &model));
    if (printModel) PetscCall(DMPlexEGADSPrintModel_Internal(model));
  }
  if (tessModel) PetscCall(DMPlexCreateEGADS_Tess_Internal(comm, context, model, dm));
  else if (newModel) PetscCall(DMPlexCreateEGADS(comm, context, model, dm));
  else PetscCall(DMPlexCreateEGADS_Internal(comm, context, model, dm));
  PetscFunctionReturn(PETSC_SUCCESS);
#else
  SETERRQ(comm, PETSC_ERR_SUP, "This method requires EGADS support. Reconfigure using --download-egads");
#endif
}
